package com.zywl.utils.utils;

import com.zywl.web.controller.form.AwakenPayForm;
import lombok.SneakyThrows;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
public class WxPayUtil {

    private KeyStore store;
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private static final Random RANDOM = new SecureRandom();
    private final Object lock = new Object();
    /**
     * 获取公私钥.
     *
     * @param keyPath  the key path
     * @param keyAlias the key alias
     * @param keyPass  password
     * @return the key pair
     */
    public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) {
        ClassPathResource resource = new ClassPathResource(keyPath);
        char[] pem = keyPass.toCharArray();
        try {
            synchronized (lock) {
                if (store == null) {
                    synchronized (lock) {
                        store = KeyStore.getInstance("PKCS12");
                        store.load(resource.getInputStream(), pem);
                    }
                }
            }
            X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias);
            certificate.checkValidity();
            // 证书的序列号
//	            String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
            // 证书的 公钥
            PublicKey publicKey = certificate.getPublicKey();
            // 证书的私钥
            PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem);

            return new KeyPair(publicKey, storeKey);

        } catch (Exception e) {
            throw new IllegalStateException("Cannot load keys from store: " + resource, e);
        }
    }

    /**
     * V3  SHA256withRSA 签名.
     *
     * @param method       请求方法  GET  POST PUT DELETE 等
     * @param canonicalUrl 例如  https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 ——> /v3/pay/transactions/app?version=1
     * @param timestamp    当前时间戳   因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致
     * @param nonceStr     随机字符串  要和TOKEN中的保持一致
     * @param body         请求体 GET 为 "" POST 为JSON
     * @param keyPair      商户API 证书解析的密钥对  实际使用的是其中的私钥
     * @return the string
     */
    @SneakyThrows
    public String requestSign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair)  {
        String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(keyPair.getPrivate());
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }

    @SneakyThrows
    public String awakenPaySign(String appid, long timestamp, String nonceStr, String body, KeyPair keyPair)  {
        String signatureStr = Stream.of(appid,String.valueOf(timestamp),  nonceStr, body)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(keyPair.getPrivate());
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }

    /**
     * 生成Token.
     *
     * @param mchId 商户号
     * @param nonceStr   随机字符串
     * @param timestamp  时间戳
     * @param serialNo   证书序列号
     * @param signature  签名
     * @return the string
     */
    public String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) {
        final String TOKEN_PATTERN = "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\","
                + "timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"";
        // 生成token
        return String.format(TOKEN_PATTERN,
                mchId,
                nonceStr, timestamp, serialNo, signature);
    }

    public String generateNonceStr() {
        char[] nonceChars = new char[32];
        for (int index = 0; index < nonceChars.length; ++index) {
            nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
        }
        return new String(nonceChars);
    }

    /**
     * 解密响应体.
     *
     * @param apiV3Key       API V3 KEY  API v3密钥 商户平台设置的32位字符串
     * @param associatedData  response.body.data[i].encrypt_certificate.associated_data
     * @param nonce          response.body.data[i].encrypt_certificate.nonce
     * @param ciphertext     response.body.data[i].encrypt_certificate.ciphertext
     * @return the string
     * @throws GeneralSecurityException the general security exception
     */
    public String decryptResponseBody(String apiV3Key,String associatedData, String nonce, String ciphertext) {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
            byte[] bytes;
            try {
                bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
            } catch (GeneralSecurityException e) {
                throw new IllegalArgumentException(e);
            }
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }

    public KeyPair createPKCS12(){
        KeyPair createPKCS12 = this.createPKCS12("/apiclient_cert.p12",
                "Tenpay Certificate", Constants.WX_MCH_ID);
        return createPKCS12;
    }

    public String getToken(String boby){
        KeyPair createPKCS12 = this.createPKCS12("/D:\\workspace\\教育机构\\src\\main\\resources\\piclient_cert.p12",
                "Tenpay Certificate", Constants.WX_MCH_ID);
        String nonceStr = this.generateNonceStr();//随机串
        long timestamp = System.currentTimeMillis()/1000;//时间戳
        String sign = this.requestSign("POST", Constants.WX_PAY_V3,
                timestamp, nonceStr,
                boby
                , createPKCS12);//签名
        String token = this.token(Constants.WX_MCH_ID, nonceStr, timestamp,
                Constants.WX_SERIAL_ID, sign);
        return token;
    }

    public String getToken(String body,String method,String url) {
        WxPayUtil wxPayUtil = new WxPayUtil();
        //1.加载证书
        KeyPair keyPair = wxPayUtil.createPKCS12("/apiclient_cert.p12",
                "Tenpay Certificate", Constants.WX_MCH_ID);
        String nonceStr = wxPayUtil.generateNonceStr();
        long timestamp = System.currentTimeMillis()/1000;
        System.out.println(url);
        //2.获取签名
        String sign = wxPayUtil.requestSign(method, url, timestamp, nonceStr,
                body,keyPair);
        //3.封装token
        String token = wxPayUtil.token(Constants.WX_MCH_ID,nonceStr,timestamp,Constants.WX_SERIAL_ID, sign);
        return token;
    }

    public AwakenPayForm getAwakenPaySign(String body) {
        //1.加载证书
        KeyPair keyPair = this.createPKCS12("/apiclient_cert.p12",
                "Tenpay Certificate", Constants.WX_MCH_ID);
        AwakenPayForm awakenPayDTO = new AwakenPayForm();
        String nonceStr = this.generateNonceStr();
        long timestamp = System.currentTimeMillis()/1000;
        //2.获取签名
        String sign = this.awakenPaySign(Constants.WX_MCH_ID, timestamp, nonceStr, body, keyPair);
        //3.封装token
        awakenPayDTO.setNonceStr(nonceStr);
        awakenPayDTO.setPackageInfo(body);
        awakenPayDTO.setSignType("RSA");
        awakenPayDTO.setTimeStamp(timestamp);
        awakenPayDTO.setPaySign(sign);
        return awakenPayDTO;
    }

    public static void main(String[] args) {
        WxPayUtil KeyPairFactory = new WxPayUtil();
        KeyPair createPKCS12 = KeyPairFactory.createPKCS12("/apiclient_cert.p12",
                "Tenpay Certificate", "1606182703");
        String nonceStr = KeyPairFactory.generateNonceStr();//随机串
        long timestamp = System.currentTimeMillis()/1000;//时间戳
        String sign = KeyPairFactory.requestSign("GET", "/v3/certificates",
                timestamp, nonceStr,
                ""
                , createPKCS12);//签名
        String token = KeyPairFactory.token("1606182703", nonceStr, timestamp,
                "7CD950B1398ABD865122D124D78B5BAC68CD89D7", sign);

        System.out.println(token);
    }

}
